<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- 
(defun add-api-entry ()
  (interactive)
  (let ((function (read-string "Function/Method name: "))
	(args (read-string "Argument list: "))
	(type (read-string "Function, Method, or Macro? ")))
    (insert (format "
    <dt>
      <span class=\"term\"><b>%s</b> <i>%s</i></span>
      <span class=\"type\">[%s]</span>
    </dt>
    <br class=\"both\" />
    <dd>
    </dd>" function args type))
    (previous-line 1)))
-->


<html>
  <head>
    <title>TRIVIAL-LDAP v0.91</title>

    <style type="text/css">
      .leftcontent {float: left; width: 75%; padding-left: 12px;}
      div.head {background-color: #aeffee;}
      span.term {float: left; text-align: left;}
      span.type {float: right; text-align: right; font-weight: bold;}
      br.both {clear: both;}
      dd {font-style: normal; padding-bottom: 8px;}
      
    </style>
  </head>

  <body class="leftcontent">
    <h3>TRIVIAL-LDAP v0.91</h3>

    TRIVIAL-LDAP is a one file, all Common Lisp client implementation of
    parts of RFC 2261.

    <h4>WARNING:</h4> This is beta software.  While I believe the API
    documented below is correct it is subject to change.  Also, <b>do
    not run execute the example.lisp program against a production LDAP
    directory, it will delete your entries.</b>  

    <h4>Introduction</h4> This LDAP client came about as a result of
    some functionality I required for a different project altogether.
    As a result it provides functionality that is general enough but
    probably not in typical low-level API fashion.  (For instance, a
    "real" LDAP library would probably tackle the BER encoding
    separately.)  However, as there is a lack of Common Lisp LDAP
    clients out there I thought I'd share.  <p/>

    I am open to both requests for features and suggestions for
    improving my Lisp code.  There are features I will be implementing
    in the short term but for the sake of not creating false
    expectations neither a comprehensive list nor timeframe is
    available. <p />

    You can reach me at montuori@gmail.com.  

  <h4>Changes</h4>
    <dl>
      <dt>2009-08-16 (v0.91)</dt>
      <dd>Applied a patch supplied by Nick Dannenberg to fix UTF-8
      handling for SBCL and CCL.  My sincerest appreciation for taking
      the time to generate and send the patch along!</dd>

      <dt>2009-03-12 (v0.90)</dt>
      <dd>Applied patches kindly provided by Slawek Zak.
	<ul>
	  <li>Enable UTF-8 encoding for Allegro.</li>
	  <li>Fixed a compilation failure on Allegro where a number
	    of constants were not properly defined.</li>
	  <li>Fixed the dosearch macro for cases where the LDAP 
	    variable is lexical.</li>
	</ul>
	Thanks to Slawek for taking the time to send the patches!
      </dd>


      <dt>2007-01-12 (v0.80)</dt>
      <dd><ul>
	  
	  <li>Converted from trivial-socket to usocket: indications
	    were that trivial-socket has been deprecated.</li>

	  <li>Added support for SSL with CL+SSL.  Setting the ldap
	  object slot <code>sslflag</code> to T or
	  the <code>port</code> slot to 636 will force an encrypted
	  connection.  Do note that this is not TLS, just ldaps://
	  ... I don't require TLS; if you do, drop me a line.</li>

	  <li>Added functionality to ease the pain of short slapd
	  idletimeouts.  The ldap object
	  slot <code>reuse-connection</code> may be set to NIL (the
	  default) to not attempt reopening connections, T to reopen
	  the connection before each request, or TRIVIAL-LDAP:REBIND
	  if the connection should be opened and a bind message sent
	  before each request (except, of course, the bind request
	    itself).  </li>

	  <li>A couple of documentation and logic bugs were corrected</li>
	</ul></dd>

    </dl>
<p>
    
  <h4>Acknowledgments</h4> I would like to thank Zach Beane for his
  many helpful comments and suggestions.  Thanks also to Ed Symanzik
  for pointing out a number inconsistencies in the code and
  documentation.  
<p>
  Also, a nod to my client, Brandeis University, for not fretting too
  much when I break out emacs + slime to administrate their LDAP
  directories.
<p>
    <h4>License</h4> TRIVIAL-LDAP is distributed under the Clarified
    Artistic License, a copy of which is included in the downloadable
    package.  

    <h4>Requirements</h4> TRIVIAL-LDAP has been tested under OpenMCL 1.0
    (OS X/PPC), SBCL 0.9.7 (OS X/PPC), and SBCL 1.0 (OS X/Intel).
<p>
    I would assume any CL that is supported by usockets and CL+SSL
    would have no issues with this code.  If you encounter problems
    let me know.

    <p/>Two external packages, usocket and CL+SSL, are required.
    CL+SSL itself requires trivial-gray-streams, flexi-streams, and
    cffi.  These may be downloaded from:

   <ul>
     <li><a href="http://common-lisp.net/project/usocket/">
	 http://common-lisp.net/project/usocket/</a></li>

     <li><a href="http://common-lisp.net/project/cl-plus-ssl/">
	 http://common-lisp.net/project/cl-plus-ssl/</a></li>

     <li><a href="http://common-lisp.net/project/cffi/">
	 http://common-lisp.net/project/cffi/</a></li>

     <li><a href="http://weitz.de/flexi-streams/">
	 http://weitz.de/flexi-streams/</a></li>
   </ul>

   The trivial-gray-streams project is part of the CL+SSL project.
   (Note: to get CL+SSL working on OS X/Intel it was necessary to
   re-compile openssl with the -m64 flag.)

<p>

    <h4>Limitations</h4>
    
    Missing functionality, as specified by RFC 2251,  includes:
    
    <ul>
      <li>UTF-8 is not implemented for all CL implementations</li>
      <li>SASL authentication is not implemented</li>
      <li>controls are not implemented</li>
      <li>extended DN searches are not implemented</li>
      <li>referrals are not followed</li>
      <li>extended request/response is not implemented</li>
    </ul>
    
    I do not require this functionality myself, but if there was
    interest I would consider augmenting TRIVIAL-LDAP with some of
    this missing functionality.

    <p/>
      
    <h4>API</h4>
    <dl>
<p><b>LDAP object and associated methods.</b></p>

      <dt>
	<span class="term"><b>new-ldap</b> <i>&key (host "localhost")
	(port 389) user pass base debug</i></span> <span
	class="type">[function]</span>
      </dt>
    <br class="both" />
    <dd>Return an LDAP object.</dd>
    
    <dt>
      <span class="term"><b>host</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Get/set the host slot value of
    ldap-object.  Slot value will be a string.</dd>

    <dt>
      <span class="term"><b>port</b> <i>ldap-object</i></span> <span
      class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Get/set the port slot value of
    ldap-object.  Slot value will be an integer.  If the sslflag slot
    is NIL, defaults to 389; if sslflag slot is T, defaults to 636.
    If the port parameter is manually set to 636 an SSL connection is
    used unless the sslflag is explicitly set to nil.
    </dd>


    <dt>
      <span class="term"><b>user</b> <i>ldap-object</i></span> <span
      class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Get/set the user DN to bind as.  Slot
    value will be a string.
    </dd>
  

    <dt>
      <span class="term"><b>pass</b> <i>ldap-object</i></span> <span
      class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Get/set the simple password to bind with.
    Slot value will be a string.
    </dd>


    <dt>
      <span class="term"><b>base</b> <i>ldap-object</i></span> <span
      class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Get/set the default base DN for searches.
    Slot value will be a string.
    </dd>


    <dt>
      <span class="term"><b>debug</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" /> 

    <dd>Get/set the debug flag.  Slot value will be T or NIL.  When
    value is T debug output will be written to *debug-io*.
    </dd>


    <dt>
      <span class="term"><b>sslflag</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" />
    <dd>Get/set the SSL flag.  When T the default port is 636 and a
    CL+SSL stream is used.  Defaults to nil.  Note that if the port
    slot is explicitly set to 636 an SSL connection is used unless the
    sslflag slot is set to nil explicitly.
    </dd>


    <dt>
      <span class="term"><b>reuse-connection</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" />
    <dd>Get/set the reuse-connection slot parameter.  When T re-open
    the stream before each reqest; when LDAP:REBIND open the stream
    and send an (ldap:bind ldap-object) message; when nil, don't do
    anything special.  Set to NIL by default.
    </dd>

<p><b>Entry object and associated methods</b></p>

    <dt>
      <span class="term"><b>new-entry</b> <i>dn &key (attrs ())
      (infer-rdn t)</i></span> <span class="type">[function]</span>
    </dt>
    <br class="both" /> 

    <dd>Return a new entry object with the DN specified.
    <code>attrs</code>, if specified, will be an alist of
    attribute/value pairs.  If <code>infer-rdn</code> is T the RDN
    attribute and value will be appended to the attribute list.
    </dd>

    
    <dt>
      <span class="term"><b>dn</b> <i>entry-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" />
    <dd>Return the DN value for entry-object.  
    </dd>


    <dt>
      <span class="term"><b>rdn</b> <i>entry-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" />
    <dd>Return the RDN value for entry-object
    </dd>    

    <dt>
      <span class="term"><b>change-rdn</b> <i>entry-object
      new-rdn</i></span> <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Change the RDN (and therefore DN) of an
    entry-object.  The RDN attribute and value will be updated in the
    attribute list.  No LDAP directory transactions will take place.
    </dd>


    <dt>
      <span class="term"><b>attr-list</b> <i>entry-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Return the list of attribute symbols
    belonging to entry-object
    </dd>


    <dt>
      <span class="term"><b>attr-value</b> <i>entry-object
      attr</i></span> <span class="type">[method]</span>
     <br class="both" />
     <span class="term"><b>attr-value</b> <i>entry-object
      attr-list</i></span> <span class="type">[method]</span>
   
    </dt>
    <br class="both" /> <dd>Return a list of values associated with
    the attribute <code>attr</code>.  Return NIL if the attribute does
    not exist.  If a list of attributes is passed, return a list of
    lists of values.
    </dd>


    <dt>
      <span class="term"><b>add-attr</b> <i>entry-object attr
      values</i></span> <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Add the specified attribute with the value
    or values specified to the entry object's attribute list.  Returns
    the entire list of attributes/values.  No LDAP directory transactions
    will take place.
    </dd>

    
    <dt>
      <span class="term"><b>del-attr</b> <i>entry-object attr
      &optional values</i></span> <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Delete the specified attribute (or values
    for attribute) from the entry object's attribute list.  Returns
    the entire list of attributes/values.  No LDAP directory
    transactions will take place.
    </dd>


    <dt>
      <span class="term"><b>replace-attr</b> <i>entry-object attribute
      values</i></span> <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Replace current attribute values with
    values specified.  Returns the entire list of attributes/values.
    No LDAP directory transactions will take place.
    </dd>



    <dt>
      <span class="term"><b>ldif</b> <i>entry-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" />
    <dd>Return an LDIF representation of entry (as a string).
    </dd>



<p><b>LDAP Commands</b></p>
<p>
<b>NB:</b> Methods below that are specialized first on ldap-object will
return three values: T/NIL indicating success or failure, the LDAP
response code (these are exported from the TRIVIAL-LDAP package as
symbols), and third, any response message received from the LDAP
directory server (as a string).
</p>
<p>
Methods specialized first on entry-object will return T (or T/NIL
in the case of compare) and will throw an ldap-response-error if a
response from the LDAP directory server is other than succesful.
</p>
<p>
<i>dn-or-entry</i> can be either an entry-object or a DN string.
<i>dn</i> is a string.  Generally if the method only requires the DN
either the entry or the DN will be acceptable arguments.
</p>



    <dt>
      <span class="term"><b>bind</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" />
    <dd>Send a bind request to the LDAP directory server.
    </dd>


    <dt>
      <span class="term"><b>unbind</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Send an unbind request to the LDAP
    directory server and close the stream.
    </dd>

<dt>
      <span class="term"><b>abandon</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Send an abandon request to the LDAP
    directory server and purge any data on the stream waiting to be
    read.  </dd>


    <dt>
      <span class="term"><b>add</b> <i>ldap-object entry-object</i></span>
      <span class="type">[method]</span>
    <br class="both" /> 
      <span class="term"><b>add</b> <i>entry-object ldap-object</i></span>
    </dt>
    <br class="both" /> <dd>Add an entry described by entry-object to
    the directory specified by ldap-object.
    </dd>



    <dt>
      <span class="term"><b>delete</b> <i>ldap-object
      dn-or-entry</i></span> <span class="type">[method]</span>
     <br class="both" />
      <span class="term"><b>delete</b> <i>entry-object
      ldap-object</i></span> 
     <br class="both" />
      <span class="term"><b>delete</b> <i>dn ldap-object</i></span>

    </dt>
    <br class="both" /> <dd>Delete entry from directory specified by
    ldap-object.  <code>dn-or-entry</code> may be an entry object or a
    DN string.  
    </dd>

    <dt>
      <span class="term"><b>moddn</b> <i>ldap-object dn-or-entry
      new-rdn &key delete-old new-sup</i></span> <span
      class="type">[method]</span>
    <br class="both" />
      <span class="term"><b>moddn</b> <i>entry-object ldap-object
      new-rdn &key delete-old new-sup</i></span> 
    <br class="both" />
      <span class="term"><b>moddn</b> <i>dn ldap-object new-rdn &key
      delete-old new-sup</i></span> 
    </dt>
    <br class="both" /> <dd>Modify the RDN specified by
    <code>dn-or-entry</code> to the replacement RDN
    <code>new-rdn</code>.  <code>dn-or-entry</code> may be either an
    entry object or DN string.  If an object is specified, the DN and
    attribute associated with the RDN slot values are modified as well
    as the directory specified by ldap-object.
    </dd>
 
    <dt>
      <span class="term"><b>compare</b> <i>ldap-object dn-or-entry
      attribute value</i></span> <span class="type">[method]</span>
      <br class="both" /> <span class="term"><b>compare</b>
      <i>entry-object ldap-object attribute value</i>
    </dt>
    <br class="both" /> <dd>Send a compare message to the directory
    server asserting that entry-obj (or DN) has an attribute
    <code>attribute</code> with a value <code>value</code>.  Returns
    either T or NIL (as the only argument or as the first argument)
    indicating a return code of compareTrue or compareFalse.  
    </dd>



    <dt>
      <span class="term"><b>modify</b> <i>ldap-object dn-or-entry
      list-of-mods</i></span> <span class="type">[method]</span>
    <br class="both" />
      <span class="term"><b>modify</b> <i>entry-object ldap-object
      list-of-mods</i></span> 
    </dt>
    <br class="both" /> <dd>Modify the entry specified by
    <code>dn-or-entry</code> or <code>entry-object</code>.
    <code>list-of-mods</code> is a list of (type attribute value)
    triples.  Type will be one of ldap:add, ldap:delete, or
    ldap:replace.
    </dd>


    <dt>
      <span class="term"><b>search</b> <i>ldap-object filter &key base
      (scope 'sub) (deref 'never) (size-limit 0) (time-limit 0)
      types-only attributes</i></span> <span
      class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>Search the directory specified by
    ldap-object.  <code>filter</code> is an LDAP filter (as a string).
    The outer parens on the filter are optional.  The search base
    defaults to the base slot-value of the ldap-object.  This method
    returns T or NIL, indicating results pending or not, respectively.
    </dd>


    <dt>
      <span class="term"><b>next-search-result</b> <i>ldap-object</i></span>
      <span class="type">[method]</span>
    </dt>
    <br class="both" /> <dd>After an ldap:search is executed, this
    method can be used to return each of the entry objects that search
    resulted in.  next-search-result will return NIL if no more
    results are available.
    </dd>


    <dt>
      <span class="term"><b>dosearch</b> <i>var search-form &body
      body</i></span> <span class="type">[macro]</span>
    </dt>
    <br class="both" /> <dd>Execute the ldap:search provided as
    <code>search-form</code> and iterate through the results binding
    each resulting entry object to <code>var</code> before evaluating
    <code>body.</code> Returns the number of entries processed.
    <b>NB: </b>If the body of the loop involves LDAP transactions it
    is probably best that a difference LDAP instance be employed.

    </dd>

    <dt>
      <span class="term"><b>ldif-search</b> <i>&rest
      ldap-search-args</i></span> <span class="type">[macro]</span>
    </dt>
    <br class="both" /> <dd>Prints the results of a search in LDIF.
    This macro is probably most useful in an interactive setting.
    <code>ldap-search-args</code> are passed directly to the search
    method described above.
    </dd>

</dl>


    <hr>
    <address><a href="mailto:montuori@gmail.com">kevin montuori</a></address>
<!-- Created: Sun Jan 22 14:36:42 EST 2006 -->
<!-- hhmts start -->
Last modified: Sat Jan 13 09:01:44 EST 2007
<!-- hhmts end -->
  </body>
</html>
